package com.jack.lib.core.utils;

import android.app.Activity;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;


/**
 * author : jack(黄冲)
 * e-mail : 907755845@qq.com
 * create : 2020/11/4
 * desc   :
 */
public class JTouchHelp {

    private static final Handler sHandler = new Handler(Looper.getMainLooper());
    private static long DEFAULT_DURATION = 2000;

    /**
     * author  : chong.huang
     * time    : 2021/11/12 10:21
     * desc    : 模拟点击
     */
    public static void click(View view, float x, float y) {
        event(view, createDown(x, y));
        event(view, createUp(x, y));
    }


    public static void click(Activity activity, float x, float y) {
        event(activity, createDown(x, y));
        event(activity, createUp(x, y));
    }


    /**
     * author  : chong.huang
     * time    : 2021/11/12 10:22
     * desc    : 模拟滑动
     * @param view   滑动的 view
     * @param startX 起始位置 x
     * @param startY 起始位置 y
     * @param endX   终点位置 x
     * @param endY   终点位置 y
     */
    public static void scroll(View view, int startX, int startY, int endX, int endY) {
        scroll(view, startX, startY, endX, endY, DEFAULT_DURATION);
    }


    public static void scroll(Activity activity, int startX, int startY, int endX, int endY) {
        scroll(activity, startX, startY, endX, endY, DEFAULT_DURATION);
    }

    /**
     * author  : chong.huang
     * time    : 2021/11/12 10:32
     * desc    : 模拟滑动
     */
    public static void scroll(View view, float startX, float startY, float endX, float endY, long duration) {
        new EventHandler(view, new CoordBean(startX, startY, endX, endY, duration));
    }

    public static void scroll(Activity activity, float startX, float startY, float endX, float endY, long duration) {
        new EventHandler(activity, new CoordBean(startX, startY, endX, endY, duration));
    }


    public static void scroll(Activity activity, Path path, long duration, int period) {
        PathHandler handler = new PathHandler(activity);
        PathBean bean = new PathBean(path, duration, period);
        Message.obtain(handler, 1, bean).sendToTarget();
    }


    /**
     * author  : chong.huang
     * time    : 2021/11/12 10:51
     * desc    : 创建事件
     */
    public static MotionEvent createEvent(int action, float x, float y) {
        return MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), action, x, y, 0);
    }

    /**
     * author  : chong.huang
     * time    : 2021/11/15 10:47
     * desc    : 创建多指事件 (目前有问题, 不能被系统的识别, 慎用!)
     */
    public static MotionEvent createPointer(int action, float x, float y) {
        MotionEvent.PointerCoords[][] touches = new MotionEvent.PointerCoords[2][];
        touches[0] = new MotionEvent.PointerCoords[1];
        touches[0][0] = new MotionEvent.PointerCoords();
        touches[0][0].x = x;
        touches[0][0].y = y;
        touches[0][0].pressure = 1;

        touches[1] = new MotionEvent.PointerCoords[1];
        touches[1][0] = new MotionEvent.PointerCoords();
        touches[1][0].x = x;
        touches[1][0].y = y;
        touches[1][0].pressure = 1;

        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[touches.length];
        MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[touches.length];

        for (int i = 0; i < touches.length; i++) {
            MotionEvent.PointerProperties prop = new MotionEvent.PointerProperties();
            prop.id = i;
            prop.toolType = MotionEvent.TOOL_TYPE_FINGER;
            properties[i] = prop;
            pointerCoords[i] = touches[i][0];
        }
        MotionEvent event = null;
        for (int i = 1; i < touches.length; i++) {
            event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
                    getPointerAction(action, i), i + 1, properties,
                    pointerCoords, 0, 0, 1, 1, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
        }
        int pointerCount = event.getPointerCount();
        int actionIndex = event.getActionIndex();

        int masked = event.getAction() & 0xff;
        for (int i = 0; i < pointerCount; i++) {
            float x1 = event.getX(i);
            float y1 = event.getY(i);
        }
        return event;
    }

    private static int getPointerAction(int motionEnvent, int index) {
        return motionEnvent + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
    }


    public static MotionEvent createDown(float x, float y) {
        return createEvent(MotionEvent.ACTION_DOWN, x, y);
    }

    public static MotionEvent createMove(float x, float y) {
        return createEvent(MotionEvent.ACTION_MOVE, x, y);
    }

    public static MotionEvent createUp(float x, float y) {
        return createEvent(MotionEvent.ACTION_UP, x, y);
    }

    public static MotionEvent createCancel(float x, float y) {
        return createEvent(MotionEvent.ACTION_CANCEL, x, y);
    }

    public static MotionEvent createOutside(float x, float y) {
        return createEvent(MotionEvent.ACTION_OUTSIDE, x, y);
    }

    public static void event(View view, MotionEvent event) {
        view.dispatchTouchEvent(event);
        event.recycle();
    }

    public static void event(Activity activity, MotionEvent event) {
        activity.dispatchTouchEvent(event);
        event.recycle();
    }


    public static void down(View view, float x, float y) {
        MotionEvent event = createDown(x, y);
        view.onTouchEvent(event);
        event.recycle();
    }

    public static void move(View view, float x, float y) {
        MotionEvent event = createDown(x, y);
        view.onTouchEvent(event);
        event.recycle();
    }

    public static void up(View view, float x, float y) {
        MotionEvent event = createDown(x, y);
        view.onTouchEvent(event);
        event.recycle();
    }

    public static void cancel(View view, float x, float y) {
        MotionEvent event = createDown(x, y);
        view.onTouchEvent(event);
        event.recycle();
    }

    public static void outside(View view, float x, float y) {
        MotionEvent event = createDown(x, y);
        view.onTouchEvent(event);
        event.recycle();
    }


    public static void doubleClick(View view, float x, float y) {
        click(view, x, y);
        view.postDelayed(() -> click(view, x, y), 150);
    }

    public static void doubleClick(Activity activity, float x, float y) {
        click(activity, x, y);
        sHandler.postDelayed(() -> click(activity, x, y), 150);
    }


    /**
     * author  : chong.huang
     * time    : 2021/11/12 10:57
     * desc    : 双指缩放
     * @param from 距离相对View大小的比例  0 - 1,  比如View 宽度500, 想移动100像素, 则为 0.2f
     */
    public static void scale(View view, long duration, @FloatRange(from = 0, to = 1) float from, @FloatRange(from = 0, to = 1) float to) {
        int width = view.getWidth();
        int height = view.getHeight();

        List<CoordBean> coordBeans = new ArrayList<>();
        {
            float sx = width * from;
            float sy = height * from;
            float ex = width * to;
            float ey = height * to;
            coordBeans.add(new CoordBean(sx, sy, ex, ey, duration));
        }
        {
            float sx = width - width * from;
            float sy = height - height * from;
            float ex = width - width * to;
            float ey = height - height * to;
            coordBeans.add(new CoordBean(sx, sy, ex, ey, duration));
        }
        new EventHandler(view, coordBeans);
    }

    private static class EventHandler extends Handler {

        private WeakReference<View> mView;
        private WeakReference<Activity> mActivity;

        EventHandler(View view, CoordBean coordBean) {
            super(Looper.getMainLooper());
            List<CoordBean> coordBeans = new ArrayList<>();
            coordBeans.add(coordBean);
            mView = new WeakReference<>(view);
            Message message = new Message();
            message.obj = coordBeans;
            sendMessage(message);
        }

        EventHandler(Activity activity, CoordBean coordBean) {
            super(Looper.getMainLooper());
            List<CoordBean> coordBeans = new ArrayList<>();
            coordBeans.add(coordBean);
            mActivity = new WeakReference<>(activity);
            Message message = new Message();
            message.obj = coordBeans;
            sendMessage(message);
        }


        EventHandler(View view, List<CoordBean> coordBeans) {
            super(Looper.getMainLooper());
            mView = new WeakReference<>(view);
            Message message = new Message();
            message.obj = coordBeans;
            sendMessage(message);
        }

        private void dispatch(View view, int action, List<CoordBean> coordBeans) {
            view.dispatchTouchEvent(createEvent(action, coordBeans.get(0).x, coordBeans.get(0).y));
            if (coordBeans.size() > 1) {
                for (int i = 1; i < coordBeans.size(); i++) {
                    CoordBean coordBean = coordBeans.get(i);
                    switch (action) {
                        case MotionEvent.ACTION_DOWN:
                            view.dispatchTouchEvent(createPointer(MotionEvent.ACTION_POINTER_DOWN, coordBean.x, coordBean.y));
                            break;
                        case MotionEvent.ACTION_UP:
                            view.dispatchTouchEvent(createPointer(MotionEvent.ACTION_POINTER_UP, coordBean.x, coordBean.y));
                            break;
                        default:
                            view.dispatchTouchEvent(createPointer(action, coordBean.x, coordBean.y));
                    }
                }
            }
        }

        private void dispatch(Activity activity, int action, List<CoordBean> coordBeans) {
            activity.dispatchTouchEvent(createEvent(action, coordBeans.get(0).x, coordBeans.get(0).y));
            if (coordBeans.size() > 1) {
                for (int i = 1; i < coordBeans.size(); i++) {
                    CoordBean coordBean = coordBeans.get(i);
                    switch (action) {
                        case MotionEvent.ACTION_DOWN:
                            activity.dispatchTouchEvent(createPointer(MotionEvent.ACTION_POINTER_DOWN, coordBean.x, coordBean.y));
                            break;
                        case MotionEvent.ACTION_UP:
                            activity.dispatchTouchEvent(createPointer(MotionEvent.ACTION_POINTER_UP, coordBean.x, coordBean.y));
                            break;
                        default:
                            activity.dispatchTouchEvent(createPointer(MotionEvent.ACTION_MOVE, coordBean.x, coordBean.y));
                    }
                }
            }
        }


        @Override
        public void handleMessage(@NonNull Message msg) {
            List<CoordBean> coordBeans = (List<CoordBean>) msg.obj;
            long count = coordBeans.get(0).count;
            long totalCount = coordBeans.get(0).totalCount;
            int action;
            if (count == 0) {
                action = MotionEvent.ACTION_DOWN;
            }else if (count >= totalCount) {
                action = MotionEvent.ACTION_UP;
            }else {
                action = MotionEvent.ACTION_MOVE;
            }
            if (mView != null && mView.get() != null && mView.get().isAttachedToWindow()) {
                dispatch(mView.get(), action, coordBeans);
            }
            if (mActivity != null && mActivity.get() != null && !mActivity.get().isFinishing()) {
                dispatch(mActivity.get(), action, coordBeans);
            }

            if (action != MotionEvent.ACTION_UP) {
                for (CoordBean coordBean : coordBeans) {
                    coordBean.count ++;
                    coordBean.x = coordBean.startX + coordBean.ratioX * count;
                    coordBean.y = coordBean.startY + coordBean.ratioY * count;
                }
                Message message = new Message();
                message.obj = coordBeans;
                sendMessageDelayed(message, 16);
            }
        }
    }

    static class ActivityHandler extends Handler {
        WeakReference<Activity> mActivity;

        ActivityHandler(Activity activity) {
            super(Looper.getMainLooper());
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Activity theActivity = mActivity.get();
            if (theActivity == null || theActivity.isFinishing()) {
                return;
            }
            CoordBean bean = (CoordBean) msg.obj;
            long count = bean.count;
            if (count >= bean.totalCount) {
                theActivity.dispatchTouchEvent(createUp(bean.endX, bean.endY));
            } else {
                float x = bean.startX + bean.ratioX * count;
                float y = bean.startY + bean.ratioY * count;
                theActivity.dispatchTouchEvent(createMove(x, y));
                bean.count++;
                Message message = new Message();
                message.obj = bean;
                sendMessageDelayed(message, 16);
            }
        }
    }


    static class PathHandler extends Handler {
        WeakReference<Activity> mActivity;
        private float[] mPos = new float[2];
        PathMeasure mPathMeasure = new PathMeasure();


        PathHandler(Activity activity) {
            super(Looper.getMainLooper());
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Activity theActivity = mActivity.get();
            if (theActivity == null || theActivity.isFinishing()) {
                return;
            }
            long downTime = SystemClock.uptimeMillis();
            PathBean bean = (PathBean) msg.obj;
            long count = bean.count;

            mPathMeasure.setPath(bean.path, false);
            mPathMeasure.getPosTan(mPathMeasure.getLength() / bean.totalCount * bean.count, mPos, null);

            if (count == 0){
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, mPos[0], mPos[1], 0));
            } else if (count >= bean.totalCount) {
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP, mPos[0], mPos[1], 0));
                return;
            } else {
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE, mPos[0], mPos[1], 0));
            }
            bean.count++;
            Message message = new Message();
            message.obj = bean;
            sendMessageDelayed(message, 16);
        }
    }


    static class PathBean {

        Path path;
        long totalCount;
        long count = 0;

        PathBean(Path path, long duration, int speed) {
            this.path = path;
            totalCount = duration / speed;
        }
    }


    static class CoordBean {

        float startX;
        float startY;
        float endX;
        float endY;
        //进度 0 - 1区间
        float ratioX;
        ////进度 0 - 1区间
        float ratioY;
        //总量
        long totalCount;
        //当前
        long count = 0;
        float x, y;

        CoordBean(float startX, float startY, float endX, float endY, long duration) {
            this.startX = startX;
            this.startY = startY;
            this.endX = endX;
            this.endY = endY;
            totalCount = duration / 16;
            ratioX = (endX - startX) / totalCount;
            ratioY = (endY - startY) / totalCount;
            x = startX;
            y = startY;
        }
    }

}
